/*
 * Copyright 2006-2007 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.sant

import _root_.sant._
import scala.collection.immutable.{Map,Set}
import scala.collection.mutable.{HashMap}

import org.apache.tools.ant.{Project => AntProject}

import org.apache.tools.ant._

import java.io.File

/**
 * @author Stepan Koltsov
 * @author Sergey Nebolsin
 */
class SantProject(val antProject: AntProject) extends Logger {
  def this() = this(new AntProject)
  def this(parent: SantProject, antProject: AntProject) = {
    this(antProject)
    this.parent = Some(parent)
  }
  
  var parent: Option[SantProject] = None
  var logLevel: Int = AntProject.MSG_WARN
  
  def initLogger() {
    val logger = new DefaultLogger
    logger.setMessageOutputLevel(logLevel)
    logger.setOutputPrintStream(java.lang.System.out)
    logger.setErrorPrintStream(java.lang.System.err)
    antProject.addBuildListener(logger)    
  }
  
  def init() {
    antProject.init()
    val defaultName = basedir.getName
    antProject.setName(defaultName)
    withProject {
      try {
        taskdef.ScalaTasks.defineTasks()
      } catch {
        case e => warn("could not define scala* tasks: " + e, e)
      }
      try {
        ant.SantTask.defineTask()        
      } catch {
        case e => warn("could not define sant task: " + e, e)
      }
    }
  }
  
  def initWithLogger(logLevel: Int) {
    this.logLevel = logLevel
    initLogger()
    init()
  }
    
  def name = antProject.getName()
  def name_=(name: String): Unit = antProject.setName(name)
  
  var targets: HashMap[String, SantTarget] = HashMap.empty
  var done: Set[String] = Set.empty
  
  def target(name: String, args: AntParam*)(p: => Unit) {
    // TODO add error checking for duplicate targets
    val target = SantTarget(this, name, args: _*)(p)
    antProject.addTarget(target.toAntTarget)
    if (target.default) antProject.setDefaultTarget(name)
    targets += (name -> target)
  }
  
  def task(name: String) = {
    val t = antProject.getTaskDefinitions.get(name).asInstanceOf[java.lang.Class]
    if (t eq null) throw new SantException("task " + name + " not found")
    else AntTask(antProject, name, t)
  }
  
  def dataType(name: String) = {
    val t = antProject.getDataTypeDefinitions.get(name).asInstanceOf[java.lang.Class]
    if (t eq null) throw new SantException("type " + name + " not found")
    else AntType(antProject, name, t)
  }
  
  def go(names: Seq[String]) {
    var allNames = names
    if( allNames.isEmpty ) {
      if( antProject.getDefaultTarget ne null ) allNames = List(antProject.getDefaultTarget) ++ allNames
      else throw new SantException("default target not found")
    }
    allNames.foreach( target =>
       if( !targets.contains(target) ) fail("target '" + target + "' not found")
    )
    antProject.executeTargets( 
      allNames.foldLeft( new java.util.Vector() )((vector, target) => {
          vector.add(target)
          vector
      })
    )
  }
  
  def description = antProject.getDescription()
  def basedir = antProject.getBaseDir()
  
  def log(message:String, throwable: Throwable, logLevel:Int) {
    antProject.log(message, throwable, logLevel)
  }

  def load(script: File) {
    // TODO improve error checking and so on
    try {
      SantScriptLoader.loadFromFile(script)
    } catch {
      case e => fail(e.getMessage, e)
    }
  }
  
  def fail(message: String) {
    throw new SantException(message)
  }
  
  def fail(message: String, cause: Throwable) {
    throw new SantException(message, cause)
  }
  
  def createSubProject() = {
    new SantProject(this, this.antProject.createSubProject)
  }
  
  def copyAntBuildListenersFromParent() = {
    val i = parent.get.antProject.getBuildListeners.iterator()
    while (i.hasNext) {
      antProject.addBuildListener(i.next().asInstanceOf[org.apache.tools.ant.BuildListener])
    }
  }
  
  def withProject[A](t: => A) = Ant.projectVar.withValue(this)(t)
}

object SantProject {
  
  def wrap(ap: AntProject) = {
    if (Ant.projectOption.isDefined && (Ant.project.antProject eq ap)) Ant.project
    else new SantProject(ap)
  }
  
  def empty() = {
    val p = new SantProject
    p
  }
}
